/*
 * Author: vyouzhi <vyouzhi@163.com>
 * http://www.xda.cn
 *
 * File: socketbase.c
 * Create Date: 2011-08-05 20:20:44
 *
 */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/un.h>

#include "socket_lib.h"
#include "conf_lib.h"
#include "log_lib.h"

int SetNonBlocking(int s){
    int opts;
    opts=fcntl(s, F_GETFL);
    if(opts == -1)
    {
        d_log("fcntl(sock,GETFL)");
        exit(-1);
    }
    opts |= O_NONBLOCK;
    if(fcntl(s, F_SETFL, opts) == -1)
    {
        d_log("fcntl(sock,SETFL,opts)");
        exit(1);
    }
	return 0;
}

int new_socket_unix(void) {
    int sfd;

    if ((sfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
        d_log("socket()");
        return -1;
    }
	SetNonBlocking(sfd);

    return sfd;
}

int server_socket_unix(void) {
    int sfd;
    struct linger ling = {0, 0};
    struct sockaddr_un addr;
    struct stat tstat;
    int flags =1, max_link;
    int old_umask, access_mask = 0777;
	char *path;
	
	path = conf_get("path");

    if (!path) {
        return -1;
    }

    if ((sfd = new_socket_unix()) == -1) {
        return -1;
    }

    /*
     * Clean up a previous socket file if we left it around
     */
    if (lstat(path, &tstat) == 0) {
        if (S_ISSOCK(tstat.st_mode))
            unlink(path);
    }

    setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags));
    setsockopt(sfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags));
    setsockopt(sfd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling));

    /*
     * the memset call clears nonstandard fields in some impementations
     * that otherwise mess things up.
     */
    memset(&addr, 0, sizeof(addr));

    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
    /*assert(strcmp(addr.sun_path, path) == 0);*/
    old_umask = umask( ~(access_mask&0777));
    if (bind(sfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
        close(sfd);
        umask(old_umask);
        return 1;
    }
    umask(old_umask);

	max_link = atoi(conf_get("max_link"));

    if (listen(sfd, max_link) == -1) {
        close(sfd);
        return 1;
    }

    return sfd;
}

int Set_SockOpt(int sockfd){
	socklen_t sendbuflen = 1024*1024;
    socklen_t len = sizeof(sendbuflen);
	struct linger opt;
	int nNetTimeout = 1;

	if (-1 == setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void*)&sendbuflen, len)) {
		d_log("Couldn't setsockopt(SO_REUSEADDR)\n");
		return (-1);
	}
	
	if (-1 == setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (void*)&sendbuflen, len )) {
		d_log("Couldn't setsockopt(SO_SNDBUF)\n");
		return (-1);
	}

	if (-1 == setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (void*)&sendbuflen, len )) {
		d_log("Couldn't setsockopt(SO_RCVBUF)\n");
		return (-1);
	}
	
	setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&nNetTimeout, sizeof(int));
	setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&nNetTimeout, sizeof(int));
 
	opt.l_onoff = 1;
	opt.l_linger = 60;

	if (-1 == setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &opt,
		sizeof(struct linger))){
		d_log("Coundn't setsockopt(SO_LINGER)\n");
		return (-1);
	}

	return 0;
}


int Socket_Init(void){

	int sockfd, port, max_link;
	struct sockaddr_in   server_addr;
	char *host;
	
	host = conf_get("server_ip");
	port = atoi(conf_get("server_port"));
	max_link = atoi(conf_get("max_link"));

	bzero(&server_addr, sizeof(server_addr));
	inet_pton(AF_INET, host, &(server_addr.sin_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(port);

	sockfd = socket(AF_INET, SOCK_STREAM, 0);

	if (-1 == sockfd) {return(-1);}
	
	if (-1 == Set_SockOpt(sockfd)) {
		close(sockfd);
		return (-1);
	}

	if (-1 == bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr))) {
		close(sockfd);
		return(-1);
	}
		
	if (-1 == SetNonBlocking(sockfd)){
		close(sockfd);
		return (-1);
	}
	if (-1 == listen(sockfd, max_link)) {
		close(sockfd);
		return(-1);
	}
	
	return (sockfd);
}

int Client_Init(char *host, int port){
	int flag = 1, client_fd;
	struct sockaddr_in to_server_addr;

    if((client_fd = socket(AF_INET , SOCK_STREAM , 0)) == - 1) {
        d_log("socket error") ;
        return (-1);
    }

    memset((char *)&to_server_addr, 0, sizeof(to_server_addr)) ;
    to_server_addr.sin_family = AF_INET;
    to_server_addr.sin_port = htons(port);
    to_server_addr.sin_addr.s_addr = inet_addr(host) ;

	if (setsockopt(client_fd, IPPROTO_TCP, TCP_NODELAY,
                  &flag, sizeof(flag)) == -1) {
    	d_log("Failed to set TCP_NODELAY");
		return (-1);
    }

	if(connect(client_fd, (struct sockaddr *)&to_server_addr, sizeof(struct sockaddr)) == - 1) {
        d_log("connect error") ;
        return (-1);
    }
	return client_fd;
}

int Content_Length(char *Buf){
	char Len[20];
	char find_word[] = "Content-Length:";
	int c_len, i, n, t;
	c_len = -1;
	t = 0;
	n = 0;
	Len[0] = '\0';
	for(i=0; i<strlen(Buf); i++){
		if(t == 1 && n < 20){			
			if(Buf[i] == '\r' || Buf[i] == '\n')break;
			Len[n] = Buf[i];
			n++;
			Len[n]='\0';
		}	
		if(t==0 && Buf[i]=='C'){			
			if(memcmp(&Buf[i], find_word, 13) == 0){
				t=1;
				i+=14;
			}
		}
		
	}
	if(n>0)
		c_len = atoi(Len);
	return c_len;
}

ssize_t Socket_Recv(int c_fd, char **recvBuf, char **format){
	ssize_t nread, stop, len_recv, off_set;
	char sBuf[MAXBUFFSIZE], xml[]="<?xml", *p;

	off_set = 0; 
	stop = 0;
	len_recv = 0;
	
	sBuf[0] = '\0';
	while(off_set < MAXBUFFSIZE && !stop){
		nread = recv(c_fd, sBuf + off_set, MAXBUFFSIZE - off_set, 0);
		if (nread == -1){
			if ( errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK){
				/*return (-1);
				sleep(0.001); */
				continue;
			}
			else stop = 1;
		}
		else if(nread == 0){
			d_log("nread == 0\n");
			stop = 1;
		}
		else if(nread > 0){
			sBuf[nread + off_set] = '\0';

			if(len_recv == 0){
				len_recv = Content_Length(sBuf);
				if(len_recv == -1) return (-1);
				off_set = 0;
			}
			off_set += nread;
	
			if(off_set >= len_recv) {
				stop = 1;
			}
		}
		sBuf[off_set] = '\0';
	}

	p = strstr(sBuf, xml);
	if(p==0){
        p = index(sBuf, '{');
        *format = strdup("2");
    }else{
        *format = strdup("1");
    }

    if(p==0) *recvBuf = NULL;
	else *recvBuf = strdup(p);

	return len_recv;
}

int Socket_Send(int c_fd, const char *sendBuf, int buflen){
	int tmp=-1, s_len=-1, total = buflen;
	const char *sBuf = sendBuf;
	
	while(total > 0 && sBuf != NULL){
		tmp = send(c_fd, sBuf, total, 0);

		if(tmp < 0)
		{
			if(errno == EINTR)
				break;
			if(errno == EAGAIN)
			{
				/*sleep(0.001);*/
				continue;
			}
		}	
		s_len += tmp;
		total -= tmp;
		sBuf += (size_t)tmp;
		/*printf("send tmp is %d\n", tmp);
		p += tmp;*/
	}
	return s_len;
}

void Socket_Close(int sock_fd){
	close(sock_fd);
}

/* vim: set ts=4 sw=4: */

